/**
 * Clase que se encarga de realizar la conexion con la base de datos
 * provee metodos para cargar el dominio 
 * @author  Jaime Painefilu - Juan Demo
 */
package postgres;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JOptionPane;

import modelo.*;

public class Conex_Postgres {
    String user;
    String password;    
    String host;
    String url;
    Connection conn = null;
    Statement stm;
    Esquema datosEsquema;
    PreparedStatement stTrigger;
    PreparedStatement stCheck;
    
    

    public Conex_Postgres (String server, String usuario, String contrasenia,String baseD)
    {
        this.user = usuario;
        this.password = contrasenia;
        this.host = server;
        this.url = "jdbc:postgresql://" + this.host + ":5432/"+baseD;
    }

    public int connectar(String esquema)
    {
        int sal = -1;
        try {
                Class.forName("org.postgresql.Driver");
                conn = DriverManager.getConnection(url, user, password);
                if (conn != null)
                {
                    String msj ="Conexión a base de datos "+url+"…. Exitosa! \n";
                    JOptionPane.showMessageDialog(null, msj, "Conexión Exitosa!", JOptionPane.INFORMATION_MESSAGE);
                    if (conn.getAutoCommit() )
                        conn.setAutoCommit(false);
                    stm = conn.createStatement();
                    stm.execute("set search_path = " + esquema);
                    //Sql para obtencion de triggers
                    stTrigger = conn.prepareStatement("select * from triggers where  trigger_schema = ? and event_object_table = ?");
                    //Sql para obtencion de checks,
                    //  select DISTINCT xq al borrar un esquema y crearlo nuevamente, los check anteriores no se borran de las tablas de information_schema correspondientes
                    stCheck = conn.prepareStatement ("select DISTINCT a.constraint_name,b.check_clause from table_constraints as a , check_constraints as b where a.constraint_schema = ? and a.constraint_type= 'CHECK' and a.table_name = ? and a.constraint_name = b.constraint_name");
                    //
                    sal = 0;
                }
            }
        catch(SQLException ex) {
            ex.printStackTrace();            
            String msj = "Hubo un problema al intentar conectarse con la base de datos "+url+ "\n";
            JOptionPane.showMessageDialog(null, msj+ex.getMessage(), "Error de Conexión !!", JOptionPane.INFORMATION_MESSAGE);
            sal = 1;
        }
        catch(ClassNotFoundException ex) {
            ex.printStackTrace();
            System.out.println(ex);
            sal = 1;
        }
        return sal;
    }

    public void desconnectar()
    {
        try {
            conn.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
            Logger.getLogger(Conex_Postgres.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void obtener_informacion(String esquema)
    {        
        try {
            DatabaseMetaData metaData = conn.getMetaData();
            ResultSet tablas = metaData.getTables(null, esquema, null, null);
          
            int nroTablas = 0;            
            //creamos el esquema donde almacenaremos los datos de BD            
            datosEsquema = new Esquema("",0);
            while(tablas.next())
            {
                datosEsquema.setNombre(tablas.getString("TABLE_SCHEM"));                
                String nombreTabl = tablas.getString("TABLE_NAME");
                if(tablas.getString("TABLE_TYPE").equals("TABLE"))
                {
                    nroTablas++;                 
                    //Creamos tbl donde almacenaremos los datos de la tabla actual
                    int nroAtributos = 0;
                    Tabla tbl = new Tabla(nombreTabl);
                    //Obtenemos datos de columnas
                    ResultSet columnas = metaData.getColumns(null, esquema, nombreTabl, null);                   
                    while(columnas.next())
                    {
                        nroAtributos++;
                        String tipo;
                        if(columnas.getString("TYPE_NAME").equals("varchar"))
                            tipo = columnas.getString("TYPE_NAME")+"("+columnas.getString("COLUMN_SIZE")+")";
                        else tipo = columnas.getString("TYPE_NAME");
                        if(columnas.getString("NULLABLE").equals("0")) tipo = tipo+" NOT NULL";
                        //Creamos atrib donde guardaremos datos de atributo actual
                        Atributo atrib = new Atributo(columnas.getString("COLUMN_NAME"),tipo);
                        tbl.addAtributos(atrib);
                    }
                    columnas.close();

                    // OBTENEMOS DATOS DE CLAVES PRIMARIAS
                    cargarPK(metaData, tbl, datosEsquema);
                    // OBTENEMOS DATOS DE CLAVES FORANEAS
                    cargarFK(metaData, tbl, datosEsquema);
                    // obtencion de triggers de la tabla
                    cargarTriggers(tbl, datosEsquema);
                    // obtencion de checks de la tabla 
                    cargarCheck(tbl, datosEsquema);
                    // obtencion de indices de la tabla
                    cargarIndex(metaData, tbl, datosEsquema);
                    datosEsquema.addTabla(tbl);
               } 
            }
            tablas.close();
            conn.commit();

            //carga de funciones
            cargarFunciones(metaData,datosEsquema);            
            //carga de dominios  o obtencion de dominios
            cargarDominios(datosEsquema);            
            String mensaje = "Se ejecuto lectura de Datos de esquema '"+esquema+"' en forma correcta.";
            JOptionPane.showMessageDialog(null, mensaje, "Lectura Datos "+ esquema, JOptionPane.INFORMATION_MESSAGE);

            //MOSTRAMOS DATOS DE TABLA durante PRUEBAS
            //datosEsquema.showEsquema();
        } catch (SQLException ex) {
                	
            Logger.getLogger(Conex_Postgres.class.getName()).log(Level.SEVERE, null, ex);
        }
                   
    }

    /**
     * carga las Claves Primarias de la tabla
     * @param metadata tabla esquema
     */
    private void cargarPK(DatabaseMetaData metaData, Tabla tabla, Esquema esquema){
    	  try {
                String ini_pk = "";
                String pktabla = "" ;
                ResultSet pk = metaData.getPrimaryKeys(null, esquema.getNombre(), tabla.getNombre());
                while(pk.next()){
                    ini_pk = pk.getString("PK_NAME")+" PRIMARY KEY(";
                    pktabla = pktabla + pk.getString("COLUMN_NAME")+", ";
                }
                if(pktabla.length()>0){
                    pktabla = pktabla.substring(0, pktabla.length()-2);
                    pktabla = ini_pk + pktabla +")";
                }
                tabla.setPkTabla(pktabla);
                pk.close();
                    
          } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }
    }

    /**
     * carga las Claves Foráneas de la tabla
     * @param metadata tabla esquema
     */
    private void cargarFK(DatabaseMetaData metaData, Tabla tabla, Esquema esquema){
    	  try {
                ResultSet fk = metaData.getImportedKeys(tabla.getNombre(), esquema.getNombre(), tabla.getNombre());
                    while(fk.next())
                    {
                        ForeignKey fkTabla = new ForeignKey();
                        fkTabla.setFknombre(fk.getString("FK_NAME"));
                        fkTabla.setFkcolumna(fk.getString("FKCOLUMN_NAME"));
                        fkTabla.setTblRef(fk.getString("PKTABLE_NAME")+"("+fk.getString("PKCOLUMN_NAME")+")");
                        /*
                         * Valores leidos en  ON DELETE (CASCADE:0; RESTRICT:1, SET NULL:2, NOT ACTION:3, SET DEFAULT:4)
                         *
                         *  <<  POR DEFECTO QUE NO PONGA NADA, EN EL CASO:    >>
                         *else if(on_action.equals("3"))fkTabla = fkTabla +" ON DELETE NOT ACTION";
                         */
                        String on_action = fk.getString("DELETE_RULE");
                        if(on_action.equals("0")) fkTabla.setEventAccion(" ON DELETE CASCADE");
                        else if(on_action.equals("1")) fkTabla.setEventAccion(" ON DELETE RESTRICT");
                        else if(on_action.equals("2")) fkTabla.setEventAccion(" ON DELETE SET NULL");
                        else if(on_action.equals("4")) fkTabla.setEventAccion(" ON DELETE SET DEFAULT");
                        /*
                         * Valores leidos en  ON UPDATE (CASCADE:0; RESTRICT:1, SET NULL:2, NOT ACTION:3, SET DEFAULT:4)
                         *
                         *  <<  POR DEFECTO QUE NO PONGA NADA, EN EL CASO:    >>
                         *else if(on_action.equals("3"))fkTabla = fkTabla +" ON UPDATE NOT ACTION";
                         */
                        on_action = fk.getString("UPDATE_RULE");
                        if(on_action.equals("0")) fkTabla.setEventAccion(" ON UPDATE CASCADE");
                        else if(on_action.equals("1")) fkTabla.setEventAccion(" ON UPDATE RESTRICT");
                        else if(on_action.equals("2")) fkTabla.setEventAccion(" ON UPDATE SET NULL");
                        else if(on_action.equals("4")) fkTabla.setEventAccion(" ON UPDATE SET DEFAULT");
                        tabla.addFkTabla(fkTabla);
                    }
                    fk.close();

          } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }
    }

    /**
     * carga las Claves Primarias de la tabla
     * @param metadata tabla esquema
     */
    private void cargarIndex(DatabaseMetaData metaData, Tabla tabla, Esquema esquema){
    	  try {
              int posIndx = -1;
              ResultSet kUnique = metaData.getIndexInfo(null, esquema.getNombre(), tabla.getNombre(),false,false);
              while(kUnique.next()){
                  Index id = new Index();
                  posIndx = tabla.existIndex(kUnique.getString("INDEX_NAME"));
                  if(posIndx>-1){
                      id = (Index) tabla.getIndex().elementAt(posIndx);
                      tabla.delIndex(posIndx);
                      id.setRefCol(id.getRefCol()+", "+kUnique.getString("COLUMN_NAME"));
                  }
                  else{
                    id.setNombreIndex(kUnique.getString("INDEX_NAME"));
                    id.setNonUnique(kUnique.getString("NON_UNIQUE"));
                    id.setRefCol(kUnique.getString("COLUMN_NAME"));
                  }
                  tabla.addIndex(id);
              }
              kUnique.close();

          } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }
    }

     /**
     * carga los Checks de la tabla
     * @param tabla esquema
     */
    private void cargarCheck(Tabla tabla, Esquema esquema){
    	  try {
                stCheck.setString(1, esquema.getNombre());
                stCheck.setString(2, tabla.getNombre());
                ResultSet rs = stCheck.executeQuery();
                Vector<Check> vectorChecksTabla = new Vector<Check>();
                while(rs.next()){
                    String checkNombre = rs.getString("constraint_name");
                    String checkCondicion = rs.getString("check_clause");
                    String isNotNull = checkCondicion.substring(checkCondicion.length()-11,checkCondicion.length() );
                    if(!isNotNull.equals("IS NOT NULL")){
                            Check checkCorriente = new Check(checkNombre,checkCondicion);
                            vectorChecksTabla.add(checkCorriente);
                    }
                }
                tabla.setChecks(vectorChecksTabla);
                rs.close();

          } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }
    }

    /**
     * carga los triggers de la tabla
     * @param esquema
     */
    private void cargarTriggers(Tabla tabla, Esquema esquema){        
    	  try {
              //obtencion de trigger de la tabla
                    stm.execute("set search_path='information_schema'");
                    stTrigger.setString(1, esquema.getNombre());
                    stTrigger.setString(2, tabla.getNombre());
                    ResultSet rs = stTrigger.executeQuery();
                    Vector<Trigger> vectorTriggerTabla = new Vector<Trigger>();
                    while(rs.next())
                    {
                        String triggerNombre = rs.getString("trigger_name");
                        String triggerCondicion = rs.getString("condition_timing");
                        String triggerEvento = rs.getString("event_manipulation");
                        String triggerAccion = rs.getString("action_orientation");
                        String triggerStatment = rs.getString("action_statement");
                        Trigger triggerCorriente = new Trigger(triggerNombre,triggerCondicion,triggerEvento,triggerAccion,triggerStatment);
                        vectorTriggerTabla.add(triggerCorriente);
                    }
                    tabla.setTriggers(vectorTriggerTabla);
                    rs.close();

          } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }
    }

     /**
     * carga las funciones del esquema al modelo
     * @param esquema
     */
    private void cargarFunciones(DatabaseMetaData metaData, Esquema esquema){
        String nombreEsquema = esquema.getNombre();
    	  try {
              //OBTENEMOS DATOS DE FUNCIONES
                ResultSet func = metaData.getProcedures(null, nombreEsquema, null);
                while(func.next())
                {
                     //System.out.print("\n   FUNCION:  "+ func.getString("PROCEDURE_NAME"));
                     Funcion funcion = new Funcion("");
                     funcion.setNombre(func.getString("PROCEDURE_NAME"));
                     ResultSet parametros = metaData.getProcedureColumns(null,nombreEsquema, func.getString("PROCEDURE_NAME"), null);
                     while(parametros.next()){
                        Parametro param = new Parametro();
                        String ca = parametros.getString("COLUMN_TYPE");
                        if(ca.equals("1"))param.setClase("IN");
                            else if(ca.equals("2"))param.setClase("INOUT");
                            else if(ca.equals("4"))param.setClase("OUT");
                        param.setNombre(parametros.getString("COLUMN_NAME"));
                        param.setTipo(parametros.getString("TYPE_NAME"));
                        if(parametros.getString("TYPE_NAME").equals("trigger")) funcion.setReturnTrigger(true);
                        funcion.addParametro(param);
                     }
                     parametros.close();
                     esquema.addFuncion(funcion);
                }
                func.close();

          } catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }

    /**
     * carga los dominios del esquema al modelo
     * @param esquema
     */
    private void cargarDominios(Esquema esquema){
    	  try {
			stm.execute("set search_path='information_schema'");//esta linea de codigo se repite en varias partes del codigo
			PreparedStatement stCheck = conn.prepareStatement ("select check_clause from domain_constraints as a,check_constraints as b where a.domain_name = ? and b.constraint_name = a.constraint_name and b.constraint_schema="+"'"+esquema.getNombre()+"'"+" and a.domain_schema="+"'"+esquema.getNombre()+"'");
			ResultSet rstDominios = stm.executeQuery("select * from domains where domain_schema ="+"'"+esquema.getNombre()+"'");
			while(rstDominios.next()){
				String nombreDominio = rstDominios.getString("domain_name");
				Dominio dominioCorriente = new Dominio(nombreDominio,rstDominios.getString("data_type"),rstDominios.getInt("character_maximum_length"));
				stCheck.setString(1,nombreDominio);
				ResultSet rstCondicion = stCheck.executeQuery();
				rstCondicion.next();
				String condicion = rstCondicion.getString("check_clause");
				dominioCorriente.setCondicion(condicion);
				esquema.addDominio(dominioCorriente);
			}
                        rstDominios.close();
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    }
        
    

    public String getHost() 
    {
        return host;
    }

    public void setHost(String host) 
    {
        this.host = host;
    }

    public String getPassword() 
    {
        return password;
    }

    public void setPassword(String password) 
    {
        this.password = password;
    }

    public String getUser() 
    {
        return user;
    }

    public void setUser(String user) 
    {
        this.user = user;
    }

    public Esquema getDatosEsquema() {
        return datosEsquema;
    }
   
} //Class Conexion
